﻿#region

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Web.Security;
using Tateeda.ClinicalStudy.Base.Data.DBModel;
using Tateeda.ClinicalStudy.Base.Data.EF;
using Tateeda.ClinicalStudy.Base.Modules.Common;
using Tateeda.ClinicalStudy.Base.Modules.Common.Extenstions;
using Tateeda.ClinicalStudy.Base.Modules.Infrastructure.Concrete.Security;
using Tateeda.ClinicalStudy.Base.Modules.Infrastructure.Interfaces.Security;
using Tateeda.ClinicalStudy.Base.Modules.Infrastructure.Interfaces.Services;
using Tateeda.ClinicalStudy.Base.Modules.Models.User;

#endregion

#region - Legal -

///<legal>
/// Copyright © 2011, Tateeda Media Network
/// All rights reserved. http://tateeda.com
///
/// Redistribution and use in source and binary forms, with or without
/// modification, are permitted provided that the following conditions
/// are met:
///
/// - Redistributions of source code must retain the above copyright
/// notice, this list of conditions and the following disclaimer.
///
/// - Neither the name of the Tateeda Media Network, nor the names of its
/// contributors may be used to endorse or promote products
/// derived from this software without specific prior written
/// permission.
///
/// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
/// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
/// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
/// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
/// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
/// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES INCLUDING,
/// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
/// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
/// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
/// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
/// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
/// POSSIBILITY OF SUCH DAMAGE.
///</legal>

#endregion - Legal -

namespace Tateeda.ClinicalStudy.Base.Modules.Infrastructure.Concrete.Services {

    public class UserService : IUserService {
        private static readonly Dictionary<int, TimeZoneInfo> UserTimezones = new Dictionary<int, TimeZoneInfo>();
        private readonly SqlUnitOfWork _unitOfWork;

        public UserService() {
            _unitOfWork = new SqlUnitOfWork();
            MembershipService = new AccountMembershipService();
        }

        private IMembershipService MembershipService { get; set; }

        #region IUserService Members

        public virtual IUnitOfWork UnitOfWork {
            get { return _unitOfWork; }
        }

        public virtual void AddToRole(int appUserId, int roleId) {
            throw new NotImplementedException();
        }

        public virtual AppUserModel CreateAppUser(AppUserModel appUserModel) {
            AppUser user = appUserModel.ToAppUser();
            user.Site = UnitOfWork.Sites.Where(s => s.SiteId == appUserModel.SiteId).FirstOrDefault();
            UnitOfWork.AppUsers.AddObject(user);
            UnitOfWork.Commit();
            return user.ToAppUserModel();
        }

        public virtual void DeleteAppUser(int appUserId) {
            //Delete from Membership first and then delete from Appuser table
            AppUser user = UnitOfWork.AppUsers.Where(u => u.AppUserId == appUserId).FirstOrDefault();
            if (user != null) {
                aspnetUsersInRoles inRole =
                    UnitOfWork.AspNetUsersInRoles.Where(uir => uir.UserId == user.UserId).FirstOrDefault();
                if (inRole != null)
                    UnitOfWork.AspNetUsersInRoles.DeleteObject(inRole);
                aspnetMembership member =
                    UnitOfWork.AspNetMembership.Where(mem => mem.UserId == user.UserId).FirstOrDefault();
                if (member != null)
                    UnitOfWork.AspNetMembership.DeleteObject(member);

                aspnetUsers netUser = UnitOfWork.AspNetUsers.Where(nu => nu.UserId == user.UserId).FirstOrDefault();
                if (netUser != null)
                    UnitOfWork.AspNetUsers.DeleteObject(netUser);
            }

            UnitOfWork.AppUsers.DeleteObject(user);
            UnitOfWork.Commit();
        }

        public virtual AppUserModel FindUserByEmail(string email) {
            throw new NotImplementedException();
        }

        public virtual AppUserModel FindUserByName(string userName) {
            AppUser appUser =
                UnitOfWork.AppUsers.Where(au => au.aspnet_Membership.aspnet_Users.UserName == userName).FirstOrDefault();
            return appUser.ToAppUserModel();
        }

        public virtual ICollection<AppUserModel> GetAllUsers() {
            ICollection<AppUser> list =
                UnitOfWork.AppUsers.Include("Site").Include("aspnet_Membership").OrderBy(au => au.SortOrder).ToList();
            return list.Select(user => user.ToAppUserModel()).ToList();
        }

        public virtual ICollection<AppUserModel> GetAllUsers(int statusId) {
            throw new NotImplementedException();
        }

        public virtual ICollection<AppUserModel> GetSiteUsers(int siteId) {
            ICollection<AppUser> users = UnitOfWork.AppUsers.Where(u => u.SiteId == siteId).ToList();
            return users.Select(user => user.ToAppUserModel()).ToList();
        }

        public virtual AppUserModel GetUser(int appUserId) {
            AppUserModel model =
                UnitOfWork.AppUsers.Where(au => au.AppUserId == appUserId).FirstOrDefault().ToAppUserModel();
            aspnetMembership memberUser =
                UnitOfWork.AspNetMembership.Where(mem => mem.UserId == model.UserId).FirstOrDefault();
            aspnetUsers user = UnitOfWork.AspNetUsers.Where(u => u.UserId == model.UserId).FirstOrDefault();
            if (user != null) {
                model.UserName = user.UserName;
            }
            if (memberUser != null) {
                model.Email = memberUser.Email;
                model.Password = memberUser.Password;
            }
            return model;
        }

        public virtual AppUserModel GetUser(Guid appUserGuid) {
            AppUserModel model =
                UnitOfWork.AppUsers.Where(au => au.UserId == appUserGuid).FirstOrDefault().ToAppUserModel();
            aspnetMembership memberUser =
                UnitOfWork.AspNetMembership.Where(mem => mem.UserId == model.UserId).FirstOrDefault();
            aspnetUsers user = UnitOfWork.AspNetUsers.Where(u => u.UserId == model.UserId).FirstOrDefault();
            if (user != null) {
                model.UserName = user.UserName;
            }
            if (memberUser != null) {
                model.Email = memberUser.Email;
                model.Password = memberUser.Password;
            }
            return model;
        }

        public virtual void RemoveFromRole(int appUserId, int roleId) {
            throw new NotImplementedException();
        }

        public virtual void ResetPassword(int appUserId) {
            throw new NotImplementedException();
        }

        public virtual void SetAppUserStatus(int appUserId, int statusId) {
            throw new NotImplementedException();
        }

        public virtual void UpdateAppUser(AppUserModel appUserModel) {
            //Update membership if any
            aspnetMembership memberUser =
                UnitOfWork.AspNetMembership.Where(mem => mem.UserId == appUserModel.UserId).FirstOrDefault();
            //Integration process for older applications
            if (memberUser == null) {
                memberUser = new aspnetMembership();
                MembershipService.CreateUser(appUserModel.UserName, "password", appUserModel.Email);
                MembershipUser newAccount = Membership.GetUser(appUserModel.UserName);
                if (newAccount != null)
                    if (newAccount.ProviderUserKey != null)
                        memberUser.UserId = new Guid(newAccount.ProviderUserKey.ToString());
            } else {
                memberUser.Email = appUserModel.Email;
            }
            AppUser user = UnitOfWork.AppUsers.Where(au => au.AppUserId == appUserModel.AppUserId).FirstOrDefault();
            Util.CopyFromModel(appUserModel, ref user);
            user.Site = UnitOfWork.Sites.Where(s => s.SiteId == appUserModel.SiteId).FirstOrDefault();
            user.UserId = memberUser.UserId;
            UnitOfWork.Commit();
        }

        public TimeZoneInfo GetUserTimeZoneInfo(int appUserId) {
            TimeZoneInfo tz;
            var siteService = new SiteService();
            lock (UserTimezones) {
                if (!UserTimezones.Keys.Contains(appUserId)) {
                    AppUserModel user = GetUser(appUserId);
                    if (user == null) {
                        return null;
                    }
                    Site site = siteService.GetSite(user.SiteId);
                    if (site == null) {
                        return null;
                    }

                    tz = TimeZoneInfo.FindSystemTimeZoneById(site.TimeZoneId);
                    UserTimezones[appUserId] = tz;
                } else {
                    tz = UserTimezones[appUserId];
                }
            }
            return tz;
        }

        #endregion
    }
}